/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: 
 * 系统版本: 1.0.0
 * 开发人员: Frank
 * 开发时间: 2019/12/5 22:28
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.spring.arch.datasource.core.helper;

import com.spring.arch.common.db.DatasourceStatus;
import com.spring.arch.common.setup.ApplicationSetup;
import com.spring.arch.common.db.DatasourceStatus;
import com.spring.arch.common.setup.ApplicationSetup;
import com.spring.arch.datasource.core.context.DatasourceParamConfig;
import com.spring.arch.datasource.core.context.InstanceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Environment;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.hibernate.tool.schema.TargetType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.stereotype.Component;

import javax.persistence.spi.PersistenceUnitInfo;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据库操作
 *
 * @author Frank
 * @version 1.0.0.1
 * @since 2019/12/5 22:28
 */
@Slf4j
@Component
public class DBOperationHelper {

    @Qualifier(value = "instanceEntityManagerFactory")
    @Autowired
    private LocalContainerEntityManagerFactoryBean factoryBean;
    @Autowired
    private ApplicationSetup applicationSetup;

    /**
     * 初始化数据源实例
     *
     * @param config 数据源配置
     */
    public void initializeDataSource(DatasourceParamConfig config) {
        // 1、保存数据源缓存
        InstanceDataSourceProvider.addDataSource(config);
        // 2、实例化数据源信息 TODO 1、数据源脚本 2、JPA底层命令
        initInstance(config);
    }

    public void initInstance(DatasourceParamConfig config) {
        if (DatasourceStatus.WAITING_FOR_INITIALIZATION.equals(config.getStatus())) {
            log.info("执行初始化程序|start，数据源信息ParamConfig={}", config);
            // 执行初始化表结构
            createInstance(config);
            // 切换数据源
            InstanceContextHolder.setInstance(config.getSchemaName());
            // 执行初始化数据
            applicationSetup.init();
            log.info("执行初始化程序|end，数据源信息ParamConfig={}", config);
        }
    }

    public void createInstance(DatasourceParamConfig config) {
        final Metadata metadata = buildMetadata(config);
        if (metadata != null) {
            SchemaExport export = new SchemaExport();
            export.createOnly(EnumSet.of(TargetType.DATABASE), metadata);
        }
    }

    public void updateInstance(DatasourceParamConfig config) {
        final Metadata metadata = buildMetadata(config);
        if (metadata != null) {
            SchemaUpdate update = new SchemaUpdate();
            update.execute(EnumSet.of(TargetType.DATABASE), metadata);
        }
    }

    private Metadata buildMetadata(DatasourceParamConfig config) {
        // 设置当前数据源
        PersistenceUnitInfo persistenceUnitInfo = factoryBean.getPersistenceUnitInfo();
        if (persistenceUnitInfo == null) {
            log.error("Building Instance PersistenceUnitInfo is Empty");
            return null;
        }
        Map<String, Object> setting = new HashMap<>(12);
        setting.put(Environment.URL, config.getUrl());
        setting.put(Environment.USER, config.getUsername());
        setting.put(Environment.PASS, config.getPassword());
        setting.put(Environment.DRIVER, "com.mysql.cj.jdbc.Driver");
        setting.put(Environment.SHOW_SQL, true);
        setting.put(Environment.AUTOCOMMIT, true);
        setting.put(Environment.FORMAT_SQL, false);
        setting.put(Environment.HBM2DDL_AUTO, "update");
        setting.put(Environment.ENABLE_LAZY_LOAD_NO_TRANS, true);
        setting.put(Environment.BATCH_VERSIONED_DATA, false);
        setting.put(Environment.DIALECT, "org.hibernate.dialect.MySQL5InnoDBDialect");
        setting.put(Environment.PHYSICAL_NAMING_STRATEGY, "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");

        StandardServiceRegistry standardServiceRegistry = new StandardServiceRegistryBuilder()
                .applySettings(setting)
                .build();


        MetadataSources metadataSources = new MetadataSources(standardServiceRegistry);
        List<String> managedClassNames = persistenceUnitInfo.getManagedClassNames();
        for (String managedClassName : managedClassNames) {
            metadataSources.addAnnotatedClassName(managedClassName);
        }

        final MetadataBuilder metadataBuilder = metadataSources.getMetadataBuilder();
        return metadataBuilder.build();
    }
}
